#include "tsocket.h"
#include <assert.h>
#ifdef WIN32
#include <winsock2.h>
#define close			closesocket
#define socklen_t		int
#pragma comment(lib, "ws2_32.lib")
#else
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#define INVALID_SOCKET	-1
#endif
namespace tlib
{
TSocket::TSocket()
{

}
TSocket::~TSocket()
{

}

bool TSocket::EnvInit()
{
#ifdef WIN32
  WSADATA wsaData;
  if (0 != ::WSAStartup(MAKEWORD(2, 2), &wsaData))
  {
    ::WSACleanup();
    return false;
  }
#endif
  return true;
}

bool TSocket::EnvFree()
{
#ifdef WIN32
  ::WSACleanup();
#endif
  return true;
}

int TSocket::Send(int fd, char* buff, int len)
{
  int nwrite = ::send(fd, buff, len, 0);
  if (nwrite == 0)
    return -1;
  if (nwrite == -1)
  {
#ifdef WIN32
    int error = ::GetLastError();
    if (WSAEWOULDBLOCK == error)
    return 0;
#else
    if (EAGAIN == errno)
      return 0;
#endif
    return -1;
  }
  return nwrite;
}

int TSocket::Recv(int fd, char* buff, int len)
{
  int nread = ::recv(fd, buff, len, 0);
  if (nread == 0)
    return -1;
  if (nread == -1)
  {
#ifdef WIN32
    int error = ::GetLastError();
    if (WSAEWOULDBLOCK == error)
    return 0;
#else
    if (EAGAIN == errno)
      return 0;
#endif
    return -1;
  }
  return nread;
}

int TSocket::Create()
{
  return ::socket(AF_INET, SOCK_STREAM, 0);
}

int TSocket::Bind(int fd, char* host, int port)
{
  struct sockaddr_in sock_addr;
  sock_addr.sin_family = AF_INET;
  sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  sock_addr.sin_port = htons(port);
  return ::bind(fd, (struct sockaddr*) &sock_addr, sizeof(struct sockaddr)); //-1????
}

int TSocket::Listen(int fd, int backlog)
{
  return ::listen(fd, backlog);
}

int TSocket::Accept(int fd)
{
  int sin_size = sizeof(struct sockaddr_in);
  struct sockaddr_in client_addr;
  return ::accept(fd, (struct sockaddr*) &client_addr, (socklen_t*) &sin_size);
}

int TSocket::Connect(int fd, char* host, int port)
{
  struct sockaddr_in sock_addr;
  sock_addr.sin_family = AF_INET;
  sock_addr.sin_addr.s_addr = inet_addr(host);
  sock_addr.sin_port = htons(port);
  return ::connect(fd, (struct sockaddr*) &sock_addr, sizeof(struct sockaddr));
}

void TSocket::Close(int fd)
{
  ::close(fd);
  fd = INVALID_SOCKET;
}

bool TSocket::SetNonBlock(int fd, bool on)
{
#ifdef WIN32
  unsigned long ul = on ? 1 : 0;
  return 0 == ::ioctlsocket(fd, FIONBIO, (unsigned long*) &ul);
#else
  int flags = fcntl(fd, F_GETFL, 0);
  if (on)
    return 0 == fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  else
    return 0 == fcntl(fd, F_SETFL, flags & (~O_NONBLOCK & 0xffffffff));
#endif
}

bool TSocket::SetTcpDelay(int fd, bool on)
{
  int optval = on ? 1 : 0;
  return 0 == ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*) &optval, sizeof(optval));
}

bool TSocket::SetReuseAddr(int fd, bool on)
{
  int optval = on ? 1 : 0;
  return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_REUSEADDR, (char*) &optval, sizeof(optval));
}

bool TSocket::SetReusePort(int fd, bool on)
{
#ifndef SO_REUSEPORT
#define SO_REUSEPORT SO_REUSEADDR
#endif
  int optval = on ? 1 : 0;
  return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_REUSEPORT, (char*) &optval, sizeof(optval));
}

bool TSocket::SetKeepAlive(int fd, bool on)
{
  int optval = on ? 1 : 0;
  return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_KEEPALIVE, (char*) &optval, sizeof(optval));
}
}
